#pragma once
#include "CharBuffer.h"
#include <vector>
#include "..\utils\GameMath.h"

namespace ds {

typedef unsigned int CDHandle;

const uint32 UNUSED_HANDLE = 0xffffff;

struct CollisionEvent {
    CDHandle firstHandle;
    CDHandle secondHandle;
    uint32 firstID;
    uint32 secondID;
	Vec2 firstPos;
	Vec2 secondPos;
};


// ------------------------------------------
//
// ------------------------------------------
class CollisionBuffer {
    
public:
    CollisionBuffer();
    ~CollisionBuffer();
    void reset() {
        m_Buffer->reset();
    }
    uint32 size() {
        return m_Buffer->size();
    }
    void add(CDHandle firstHandle,uint32 firstID,const Vec2& firstPos,CDHandle secondHandle,uint32 secondID,const Vec2& secondPos);
    void get(uint32 index,CollisionEvent* collision);
private:
    CharBuffer* m_Buffer;
};

// ------------------------------------------
//
// ------------------------------------------
class CollisionHandler {
    
public:
    virtual void processBuffer(CollisionBuffer& buffer) = 0;
};

// ------------------------------------------
//
// ------------------------------------------
class CollisionDetector {

struct IgnoredCollision {
	CDHandle firstID;
	CDHandle secondID;
};
    
typedef std::vector<CollisionHandler*> Handlers;    
typedef std::vector<CDHandle> FreeHandles;

public:
    CollisionDetector();
    virtual ~CollisionDetector();
    CDHandle addBox(const Vec2& pos,const Vec2& extent,uint32 id);
	CDHandle addBox(const Vec2& pos,const Vec2& offset,const Vec2& extent,uint32 id);
	CDHandle addCircle(const Vec2& pos,float radius,uint32 id);
	CDHandle addCircle(const Vec2& pos,const Vec2& offset,float radius,uint32 id);
    void setActive(CDHandle handle,bool active) {
        m_States[handle] = active;
    }
    void setPosition(CDHandle handle,const Vec2& pos) {
        m_Positions[handle] = pos;
    }
	void setOffset(CDHandle handle,const Vec2& offset) {
		m_Offsets[handle] = offset;
	}
    void setExtent(CDHandle handle,const Vec2& extent) {
        m_Extents[handle] = extent;
    }
    void update();    
    void registerHandler(CollisionHandler* handler) {
        m_Handlers.push_back(handler);
    }        
    void remove(CDHandle handle);
	void reset() {
		m_CB->reset();
	}
	void processBuffer();
	void addIgnore(CDHandle first,CDHandle second) {}
private:
    //
	CDHandle add(const Vec2& pos,const Vec2& offset,const Vec2& extent,uint32 id,uint32 type);
    void intersects(const Vec2& pos,const Vec2& offset,const Vec2& extent,uint32 handle,uint32 id,uint32 type) const;
    bool checkBoxIntersection(const Vec2& p1,float w1,float h1,const Vec2& p2,float w2,float h2) const;
	bool checkCircle(const Vec2& p1, const Vec2& o1, float r1, const Vec2& p2, const Vec2& o2, float r2) const;
    //
	uint32* m_Types;
    Vec2* m_Positions;
	Vec2* m_Offsets;
    Vec2* m_Extents;
    bool* m_States;
    uint32* m_Identifiers;
    uint32 m_Index;
    Handlers m_Handlers;
    CollisionBuffer* m_CB;
	FreeHandles m_FreeHandles;
};

namespace collision {

inline bool containsID(const CollisionEvent& collision,uint32 id) {
	if ( collision.firstID == id || collision.secondID == id ) {
		return true;
	}
	return false;
}

inline CDHandle getHandleByID(const CollisionEvent& collision,uint32 id) {
	if ( collision.firstID == id ) {
		return collision.firstHandle;
	}
	return collision.secondHandle;
}

inline const Vec2& getPositionByID(const CollisionEvent& collision,uint32 id) {
	if ( collision.firstID == id ) {
		return collision.firstPos;
	}	
	return collision.secondPos;
}

inline bool isBetween(const CollisionEvent& collision,uint32 firstID,uint32 secondID) {
	if ( containsID(collision,firstID) && containsID(collision,secondID) ) {
		return true;
	}	
	return false;
}

}

}


